
const PREF_FOREGROUND = "browser.display.foreground_color";
const PREF_BACKGROUND = "browser.display.background_color";
const PREF_ANCHOR = "browser.anchor_color";
const PREF_VISITED = "browser.visited_color";
const PREF_USE_SYSTEM = "browser.display.use_system_colors";
const PREF_USE_DOCUMENT = "browser.display.use_document_colors";

const DEFAULT_BGCOLOR = "#FFFFFF";
const DEFAULT_FOREGROUND = "#000000";
const DEFAULT_ANCHOR = "#0000EE";
const DEFAULT_VISITED = "#551A8B";

/**
 * Extend Javascript's String Class.
 */
String.prototype.trim = function() {
	return this.replace(/^\s+/, '').replace(/\s+$/, '');
}

var colorArr; // color settings
var selectedIndex = 0; // the selected color setting

var doubleClickFlag = false; // doubleClick Flag
var timeoutId = null; // setTimeout's ID

// ##################### Event Handlers ############################

/**
 * Read color settings into Firefox when loading completed.
 * You cann't retrieve element in the xul file before loading completed.
 */
function QuickColorSwitcherInit() {
	
	window.addEventListener ("load", readSettings, false);
}

function clickHandler(event) {
	
	// Don't call clickLogicFunction twice when doubleClick
	if (timeoutId == null) {
		timeoutId = setTimeout(function() {clickLogicFunction(event);}, 300);
	}
}

function dbclickHandler() {
	
	doubleClickFlag = true;
}

function clickLogicFunction(event) {
	
	timeoutId = null;
	if (doubleClickFlag) {
		doubleClickFlag = false;
		openColorPanel();
	} else {
		// on windows, right click calls this method
		if (event && (event.button == 2)) return;
		switchColors();
	}
}

/**
 * Restore defaults.
 */
function restoreDefaultsCmd() {
	
	restoreDefaultSettingsFile();
	readSettings();
	setColorToSelectedOption();
}

/**
 * Export the current color settings to a file.
 */
function exportCmd() {

	var strBundle = document.getElementById("qcs_properties");
	var nsIFilePicker = Components.interfaces.nsIFilePicker;
	var fp = Components.classes["@mozilla.org/filepicker;1"].
							createInstance(nsIFilePicker);
	fp.init(window, strBundle.getString("export.dialog.title"), nsIFilePicker.modeSave);
	fp.appendFilter(strBundle.getString("quick.color.switcher.file"), "*.qcs");
	var ret = fp.show();
	if ((ret == nsIFilePicker.returnOK) || (ret == nsIFilePicker.returnReplace)) {
		var file = fp.file;
		var fileName = file.leafName;
		var matches = fileName.match(/\.qcs$/i);
		if ((matches == null) || (matches.length == 0)) {
			file.leafName = fileName + ".qcs";
		}
		if (!file.exists()) FileIO.create(file);
		FileIO.write(file, transObjToStr());
	}
}

/**
 * Import color settings from backup file.
 */
function importCmd() {

	var strBundle = document.getElementById("qcs_properties");
	var nsIFilePicker = Components.interfaces.nsIFilePicker;
	var fp = Components.classes["@mozilla.org/filepicker;1"].
							createInstance(nsIFilePicker);
	fp.init(window, strBundle.getString("import.dialog.title"), nsIFilePicker.modeOpen);
	fp.appendFilter(strBundle.getString("quick.color.switcher.file"), "*.qcs");
	var ret = fp.show();
	if ((ret == nsIFilePicker.returnOK) || (ret == nsIFilePicker.returnReplace)) {
		var file = fp.file;
		var cont = FileIO.read(file);
		var settingsFile = getSettingsFile();
		FileIO.write(settingsFile, cont);
		readSettings();
		setColorToSelectedOption();
	}
}

/**
 * Switch Color setting between default and custom.
 * 
 */
function switchColors() {

	var prefs = Components.classes["@mozilla.org/preferences-service;1"].
								getService(Components.interfaces.nsIPrefBranch);
	var bgColor = prefs.getCharPref(PREF_BACKGROUND);
	if (bgColor == DEFAULT_BGCOLOR) 
		setColorToSelectedOption();
	else setColorToDefaultOption();
}

/**
 * Open color selecting dialog.
 * 
 */
function openColorPanel() {
	
	var strBundle = document.getElementById("qcs_properties");
	window.openDialog("chrome://quickcolorswitcher/content/xul/colorDialog.xul", 
						"colorDialog", "centerscreen,modal", colorArr, strBundle);
}

/**
 * Show Help Dialog.
 * 
 */
function helpCmd() {
	
	window.openDialog("chrome://quickcolorswitcher/content/xul/help.xul", 
						"helpDialog", "centerscreen,modal");
}

/**
 * Change the current color setting with the specified option.
 * 
 */
function chooseColorOptionHandler(index) {
	
	if (index != selectedIndex) {
		setSelectedIndex(index);
		setColorToSelectedOption();
		saveSettings();
	}
}

// ##################### Common Functions ############################

const EXTENSION_ID = "QuickColorSwitcher@51ajax.net"; // Extension ID
const COLOR_SETTINGS_FILE = "qcs_color_settings.qcs"; // settings file's name

/**
 * Color value object
 * 
 */
function ColorVO() {
	
	this.foreground = PREF_FOREGROUND;
	this.background = PREF_BACKGROUND;
	this.anchor = PREF_ANCHOR;
	this.visited = PREF_VISITED;
}

/**
 * Set color setting to the selected option.
 * 
 */
function setColorToSelectedOption() {
	
	var prefs = Components.classes["@mozilla.org/preferences-service;1"].
								getService(Components.interfaces.nsIPrefBranch);
	// custom settings
	var selectedItem = getSelectedItem();
	if (selectedItem) {
		prefs.setCharPref(PREF_FOREGROUND, selectedItem.foreground);
		prefs.setCharPref(PREF_BACKGROUND, selectedItem.background);
		prefs.setCharPref(PREF_ANCHOR, selectedItem.anchor);
		prefs.setCharPref(PREF_VISITED, selectedItem.visited);
	}
	prefs.setBoolPref(PREF_USE_DOCUMENT, false);
	prefs.setBoolPref(PREF_USE_SYSTEM, false);
}

/**
 * Set color setting to the default.
 * 
 */
function setColorToDefaultOption() {
	
	var prefs = Components.classes["@mozilla.org/preferences-service;1"].
								getService(Components.interfaces.nsIPrefBranch);
	// default settings
	prefs.setCharPref(PREF_FOREGROUND, DEFAULT_FOREGROUND);
	prefs.setCharPref(PREF_BACKGROUND, DEFAULT_BGCOLOR);
	prefs.setCharPref(PREF_ANCHOR, DEFAULT_ANCHOR);
	prefs.setCharPref(PREF_VISITED, DEFAULT_VISITED);
	prefs.setBoolPref(PREF_USE_DOCUMENT, true);
	prefs.setBoolPref(PREF_USE_SYSTEM, false);
}

/**
 * Get the selected Color setting Object.
 * 
 */
function getSelectedItem() {
	
	if (selectedIndex < colorArr.length) return colorArr[selectedIndex];
	else return null;
}

/**
 * Set the selected index, and change the color of icon in the status-bar.
 * 
 */
function setSelectedIndex(index) {
	
	if (index != null) {
		// parameter's validation
		if (typeof(index) == 'string') {
			index = parseInt(index.trim());
			if (isNaN(index)) index = 0;
		} else if (typeof(index) != 'number') {
			index = 0;
		}
		// adjust the index
		if (index < 0) {
			index = 0;
		} else if (index > colorArr.length) {
			index = (colorArr.length > 0) ? (colorArr.length - 1) : 0;
		}
		// set selected index
		selectedIndex = index;
		var qcsIcon = document.getElementById("qcsBackgroundBox");
		qcsIcon.setAttribute("value", index + 1);
	}
	// set color of the icon in the status-bar
	changeIconStyleToSelected();
}

/**
 * Change the style of the icon in the status-bar to the selected option.
 * 
 */
function changeIconStyleToSelected() {

	var item = getSelectedItem();
	if (item) {
		var backBox = document.getElementById("qcsBackgroundBox");
		if (backBox) { // change color
			backBox.style.backgroundColor = item.background;
			backBox.style.color = item.foreground;
		}
	}
}

/**
 * initialize the following variable.
 * settingsFile, colorArr
 * 
 */
function readSettings() {
	
	colorArr = new Array();
	var settingsFile = getSettingsFile();
	transStrToObj(FileIO.read(settingsFile));
}

/**
 * Save the changed settings to file.
 */
function saveSettings() {
	
	var settingsFile = getSettingsFile();
	FileIO.write(settingsFile, transObjToStr());
}

/**
 * Return the extension's color settings file.
 * If not exist then create a new file with default settings.
 */
function getSettingsFile() {
	
	var em = Components.classes["@mozilla.org/extensions/manager;1"].
	         		getService(Components.interfaces.nsIExtensionManager);
	var settingsFile = em.getInstallLocation(EXTENSION_ID).
				getItemFile(EXTENSION_ID, COLOR_SETTINGS_FILE);
	if (!settingsFile.exists()) {
		restoreDefaultSettingsFile();
	}
	return settingsFile;
}

/**
 * Create a new setting file with default settings.
 */
function restoreDefaultSettingsFile() {
	
	var defaultCont = "#999999,#000000,#CCCCCC,#999999\n" + 
					"#000000,#C0C0C0,#993300,#CC6600\n" +
					"#000000,#C0DCC0,#663300,#CC6600\n" +
					"#000000,#FDE6E0,#330033,#663366\n" +
					"#000000,#E3EDCD,#660000,#990000\n" +
					"#000000,#DCE2F1,#993300,#CC6600\n" +
					"#000000,#E9EBFE,#660000,#990000\n" +
					"#000000,#EAEAEF,#663333,#996633\n" +
					"#000000,#FAF9DE,#003300,#006600\n" +
					"0";

	var em = Components.classes["@mozilla.org/extensions/manager;1"].
	         		getService(Components.interfaces.nsIExtensionManager);
	var settingsFile = em.getInstallLocation(EXTENSION_ID).
				getItemFile(EXTENSION_ID, COLOR_SETTINGS_FILE);
	if (!settingsFile.exists()) {
		FileIO.create(settingsFile);
	}
	FileIO.write(settingsFile, defaultCont);
}

/**
 * transform settings file's content to ColorVO objects,
 * and then push in colorArr.
 * 
 * @param {} content settings file's content
 */
function transStrToObj(content) {
	
	var lines = content.split("\n");
	for (var i = 0; i < lines.length; ++i) {
		var words = lines[i].split(",");
		if (words.length >= 4) {
			var obj = new ColorVO();
			obj.foreground = words[0].trim();
			obj.background = words[1].trim();
			obj.anchor = words[2].trim();
			obj.visited = words[3].trim();
			colorArr.push(obj); // read settings to array
		} else if (words.length == 1) {
			setSelectedIndex(words[0]);
		}
	}
}

/**
 * transform ColorVO objects in colorArr to string.
 * 
 */
function transObjToStr() {
	
	var ret = "";
	for (var i = 0; i < colorArr.length; ++i) {
		var colorObj = colorArr[i];
		ret += colorObj.foreground + "," + colorObj.background + ","
				+ colorObj.anchor + "," + colorObj.visited + "\n";
	}
	ret += selectedIndex;
	return ret;
}

/////////////////////////////////////////////////
/////////////////////////////////////////////////
//
// Basic JavaScript File and Directory IO module
// By: MonkeeSage, v0.1
//
/////////////////////////////////////////////////
/////////////////////////////////////////////////


if (typeof(JSIO) != 'boolean') {

	var JSIO = true;

	/////////////////////////////////////////////////
	// Basic file IO object based on Mozilla source 
	// code post at forums.mozillazine.org
	/////////////////////////////////////////////////

	// Example use:
	// var fileIn = FileIO.open('/test.txt');
	// if (fileIn.exists()) {
	// 	var fileOut = FileIO.open('/copy of test.txt');
	// 	var str = FileIO.read(fileIn);
	// 	var rv = FileIO.write(fileOut, str);
	// 	alert('File write: ' + rv);
	// 	rv = FileIO.write(fileOut, str, 'a');
	// 	alert('File append: ' + rv);
	// 	rv = FileIO.unlink(fileOut);
	// 	alert('File unlink: ' + rv);
	// }

	var FileIO = {

		localfileCID  : '@mozilla.org/file/local;1',
		localfileIID  : Components.interfaces.nsILocalFile,

		finstreamCID  : '@mozilla.org/network/file-input-stream;1',
		finstreamIID  : Components.interfaces.nsIFileInputStream,

		foutstreamCID : '@mozilla.org/network/file-output-stream;1',
		foutstreamIID : Components.interfaces.nsIFileOutputStream,

		sinstreamCID  : '@mozilla.org/scriptableinputstream;1',
		sinstreamIID  : Components.interfaces.nsIScriptableInputStream,

		suniconvCID   : '@mozilla.org/intl/scriptableunicodeconverter',
		suniconvIID   : Components.interfaces.nsIScriptableUnicodeConverter,

		open   : function(path) {
			try {
				var file = Components.classes[this.localfileCID]
								.createInstance(this.localfileIID);
				file.initWithPath(path);
				return file;
			}
			catch(e) {
				return false;
			}
		},

		read   : function(file, charset) {
			try {
				var data     = new String();
				var fiStream = Components.classes[this.finstreamCID]
									.createInstance(this.finstreamIID);
				var siStream = Components.classes[this.sinstreamCID]
									.createInstance(this.sinstreamIID);
				fiStream.init(file, 1, 0, false);
				siStream.init(fiStream);
				data += siStream.read(-1);
				siStream.close();
				fiStream.close();
				if (charset) {
					data = this.toUnicode(charset, data);
				}
				return data;
			} 
			catch(e) {
				return false;
			}
		},

		write  : function(file, data, mode, charset) {
			try {
				var foStream = Components.classes[this.foutstreamCID]
									.createInstance(this.foutstreamIID);
				if (charset) {
					data = this.fromUnicode(charset, data);
				}
				var flags = 0x02 | 0x08 | 0x20; // wronly | create | truncate
				if (mode == 'a') {
					flags = 0x02 | 0x10; // wronly | append
				}
				foStream.init(file, flags, 0664, 0);
				foStream.write(data, data.length);
				// foStream.flush();
				foStream.close();
				return true;
			}
			catch(e) {
				return false;
			}
		},

		create : function(file) {
			try {
				file.create(0x00, 0664);
				return true;
			}
			catch(e) {
				return false;
			}
		},

		unlink : function(file) {
			try {
				file.remove(false);
				return true;
			}
			catch(e) {
				return false;
			}
		},

		path   : function(file) {
			try {
				return 'file:///' + file.path.replace(/\\/g, '\/')
							.replace(/^\s*\/?/, '').replace(/\ /g, '%20');
			}
			catch(e) {
				return false;
			}
		},

		toUnicode   : function(charset, data) {
			try{
				var uniConv = Components.classes[this.suniconvCID]
									.createInstance(this.suniconvIID);
				uniConv.charset = charset;
				data = uniConv.ConvertToUnicode(data);
			} 
			catch(e) {
				// foobar!
			}
			return data;
		},

		fromUnicode : function(charset, data) {
			try {
				var uniConv = Components.classes[this.suniconvCID]
									.createInstance(this.suniconvIID);
				uniConv.charset = charset;
				data = uniConv.ConvertFromUnicode(data);
				// data += uniConv.Finish();
			}
			catch(e) {
				// foobar!
			}
			return data;
		}

	}


	/////////////////////////////////////////////////
	// Basic Directory IO object based on JSLib 
	// source code found at jslib.mozdev.org
	/////////////////////////////////////////////////

	// Example use:
	// var dir = DirIO.open('/test');
	// if (dir.exists()) {
	// 	alert(DirIO.path(dir));
	// 	var arr = DirIO.read(dir, true), i;
	// 	if (arr) {
	// 		for (i = 0; i < arr.length; ++i) {
	// 			alert(arr[i].path);
	// 		}
	// 	}
	// }
	// else {
	// 	var rv = DirIO.create(dir);
	// 	alert('Directory create: ' + rv);
	// }

	// ---------------------------------------------
	// ----------------- Nota Bene -----------------
	// ---------------------------------------------
	// Some possible types for get are:
	// 	'ProfD'				= profile
	// 	'DefProfRt'			= user (e.g., /root/.mozilla)
	// 	'UChrm'				= %profile%/chrome
	// 	'DefRt'				= installation
	// 	'PrfDef'				= %installation%/defaults/pref
	// 	'ProfDefNoLoc'		= %installation%/defaults/profile
	// 	'APlugns'			= %installation%/plugins
	// 	'AChrom'				= %installation%/chrome
	// 	'ComsD'				= %installation%/components
	// 	'CurProcD'			= installation (usually)
	// 	'Home'				= OS root (e.g., /root)
	// 	'TmpD'				= OS tmp (e.g., /tmp)

	var DirIO = {

		sep        : '/',

		dirservCID : '@mozilla.org/file/directory_service;1',
	
		propsIID   : Components.interfaces.nsIProperties,
	
		fileIID    : Components.interfaces.nsIFile,

		get    : function(type) {
			try {
				var dir = Components.classes[this.dirservCID]
								.createInstance(this.propsIID)
								.get(type, this.fileIID);
				return dir;
			}
			catch(e) {
				return false;
			}
		},

		open   : function(path) {
			return FileIO.open(path);
		},

		create : function(dir) {
			try {
				dir.create(0x01, 0664);
				return true;
			}
			catch(e) {
				return false;
			}
		},

		read   : function(dir, recursive) {
			var list = new Array();
			try {
				if (dir.isDirectory()) {
					if (recursive == null) {
						recursive = false;
					}
					var files = dir.directoryEntries;
					list = this._read(files, recursive);
				}
			}
			catch(e) {
				// foobar!
			}
			return list;
		},

		_read  : function(dirEntry, recursive) {
			var list = new Array();
			try {
				while (dirEntry.hasMoreElements()) {
					list.push(dirEntry.getNext()
									.QueryInterface(FileIO.localfileIID));
				}
				if (recursive) {
					var list2 = new Array();
					for (var i = 0; i < list.length; ++i) {
						if (list[i].isDirectory()) {
							files = list[i].directoryEntries;
							list2 = this._read(files, recursive);
						}
					}
					for (i = 0; i < list2.length; ++i) {
						list.push(list2[i]);
					}
				}
			}
			catch(e) {
			   // foobar!
			}
			return list;
		},

		unlink : function(dir, recursive) {
			try {
				if (recursive == null) {
					recursive = false;
				}
				dir.remove(recursive);
				return true;
			}
			catch(e) {
				return false;
			}
		},

		path   : function (dir) {
			return FileIO.path(dir);
		},

		split  : function(str, join) {
			var arr = str.split(/\/|\\/), i;
			str = new String();
			for (i = 0; i < arr.length; ++i) {
				str += arr[i] + ((i != arr.length - 1) ? 
										join : '');
			}
			return str;
		},

		join   : function(str, split) {
			var arr = str.split(split), i;
			str = new String();
			for (i = 0; i < arr.length; ++i) {
				str += arr[i] + ((i != arr.length - 1) ? 
										this.sep : '');
			}
			return str;
		}
	
	}

	if (navigator.platform.toLowerCase().indexOf('win') > -1) {
		DirIO.sep = '\\';
	}

}

QuickColorSwitcherInit();
